/***************************************************************************
 *   Copyright (C) 2005 by xihe                                            *
 *   xihels@163.com                                                        *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                             
 *                                             *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#ifndef _QQ_SESSION_H_
#define _QQ_SESSION_H_
#include "QQClient.h"
#include "FriendInfo.h"
#include "ClusterInfo.h"
#include "BuddyMap.h"
#include "CodeConverter.h"

#include <memory>
using std::auto_ptr;



#include "ClusterOperate.h"
extern CodeConverter g2u;

class ReceiveIMPacket;
class SendIMReplyPacket;
class FriendChangeStatusPacket;
class GetFriendOnlineReplyPacket;
class ChangeStatusReplyPacket;
class GetFriendListReplyPacket;
class GroupDataOpReplyPacket;
class DownloadGroupFriendReplyPacket;
class GetUserInfoReplyPacket;
class ClusterSendIMReplyPacket;
class GetClusterOnlineMemberReplyPacket;
class GetClusterMemberInfoReplyPacket;
class GetClusterInfoReplyPacket;
class GetFriendLevelReplyPacket;
class SearchUserReplyPacket;
class MsgWindow;
class MainWindow;
class StatusIcon;
typedef auto_ptr<MsgWindow>	    MsgWindowPtr;

class QQSession : public QQClient {
    public:
	typedef BuddyMap<BuddyInfo> Buddys;
	typedef BuddyMap<FriendInfo> Friends;
	typedef BuddyMap<ClusterInfo> Clusters;
	typedef BuddyMap<GroupInfo> Groups;
	typedef BuddyMap<QQOnlineUserInfo> Users;

	const string& getNickname() const { return nickname; }

	QQSession(const QQUser& user_);

	BuddyInfo* getClusterInfo(uint32_t uid) 
	{
	    return clusters.getElement(uid);
	}
	
	FriendInfo* getFriendInfo(uint32_t uid) 
	{
	    return friends.getElement(uid);
	}

	// 这个函数主要用在当用户双击聊天窗口列表中的成员时，
	// 把这个成员复制到主好友列表中。
	void copyToFriends(FriendInfo* friendinfo, int32_t group = STRANGER_GROUP_NUM);
	
	void showMessagePage(BuddyInfo* buddy);

	bool compare(int uid) { return getUid() == uid; }

	const Friends& getFriends() const   { return friends; }
	const Clusters& getClusters() const { return clusters; }
	const Groups& getGroups() const	    { return groups; }

	uint8_t getCurrentStatus() const    { return currentStatus; }
	void	changeStatus(uint8_t status);

	void  destroy() { delete this; }
    private:
	void showPage(BuddyInfo* buddy, bool isActive = false);
    private:
	friend class RegisterInPacket;
	~QQSession();
	
	QQSession(const QQSession& rhs);	        // 禁止拷贝
	QQSession& operator = (const QQSession& rhs);	// 禁止拷贝


    private:
	void doRecvPacket(InPacket* packet);  // 父类的接收线程调用这个函数

	void doKeepAlive(int status);
	void doLoginError(const LoginReplyPacket& packet);
	void doLoginOk();
	void doUnknow(const UnknowPacket& packet);

    public:// GCC3需要下列函数是public的
	void onSendIMReply(const SendIMReplyPacket&);
	void onClusterSendIMReply(const ClusterSendIMReplyPacket&);
	
	void onChangeStatus(const ChangeStatusReplyPacket& packet);
	void onFriendChangeStatus(const FriendChangeStatusPacket& packet);
	void onIMPacket(const ReceiveIMPacket& packet);
	void onNormalIM(const ReceiveIMPacket& packet);
	void onClusterIM(const ReceiveIMPacket& packet);

	void onUserInfo(const GetUserInfoReplyPacket& packet);
	void onSearchUser(const SearchUserReplyPacket& packet);

	void onDownloadGroupFriend(const DownloadGroupFriendReplyPacket& packet);
	void onGroupOpPacket(const GroupDataOpReplyPacket& packet);

	void onFriendListPacket(const GetFriendListReplyPacket& pack);
	void onFriendOnlinePacket(const GetFriendOnlineReplyPacket& pack);
	void onFriendLevel(const GetFriendLevelReplyPacket& pack);

	void onClusterInfo(const GetClusterInfoReplyPacket& packet);
	void onClusterMember(const GetClusterMemberInfoReplyPacket& packet);
	void onClusterOnlineMember(const GetClusterOnlineMemberReplyPacket& pack);

    private:
	template<typename T>
	    struct SetGroupsList :
		public unary_function<const typename T::value_type&, void> {

		    SetGroupsList(QQSession& _session) : session(_session) {}
		    void operator ()(const typename T::value_type& element);
		    QQSession& session;
		};
	template<typename T>
	    struct SetGroupsListName : 
		public unary_function<const typename T::value_type& ,void> {

		    SetGroupsListName(QQSession& _session) : session(_session) {}
		    void operator() (const typename T::value_type& element);
		    QQSession& session;
		};



	template <typename T>
	    struct SetBuddysList :
		public unary_function<const typename T::value_type, void> {

		    SetBuddysList(QQSession::Friends& _friends) : friends(_friends) {}

		    void operator () (const typename T::value_type& element);
		    template<typename Tt>
			uint32_t getElementId(const Tt& elem) { return elem.uid; }
		    uint32_t getElementId(uint32_t elem) { return elem; }
		    QQSession::Friends& friends;
		};
	
	template <typename T>
	    struct SetUsersList :
		public unary_function<const typename T::value_type, void> {

		    SetUsersList(QQSession::Users& _users) : users(_users) {}

		    void operator () (const typename T::value_type& element);
		    template<typename Tt>
			uint32_t getElementId(const Tt& elem) { return elem.uid; }
		    uint32_t getElementId(uint32_t elem) { return elem; }
		    QQSession::Users& users;
		};
	
	
    private:
	Friends		    friends;
	Users		    users;
	Clusters	    clusters;
	Groups		    groups;
	MsgWindowPtr	    msgWnd;
	MainWindow*	    mainWnd;
	string		    nickname;
	uint8_t		    currentStatus;   // 当前登录状态(隐身，在线，离开)
	uint8_t		    changeStatus_;   // 正在改变到的状态
	StatusIcon*	   statusIcon;
};

    template<typename T>
inline void QQSession::SetGroupsList<T>::operator()
( const typename T::value_type& element)
{
    uint32_t uid = element.uid;
    int	     groupid = element.group / 4;

    if (DEFAULT_GROUP != groupid) {
	if (NULL ==  session.groups.getElement(groupid)) {
	    session.groups.insertElement(groupid, new GroupInfo(groupid));
	}

    }

    if (QQ_ID_IS_CLUSTER == element.type) {

	if (NULL ==  session.clusters.getElement(uid)) {
	    session.clusters.insertElement(uid, new ClusterInfo(element));
	} 

	session.sendPacket(OutPacketPtr(
		    new GetClusterInfoPacket(session, uid)));

    } else {
	FriendInfo* friendinfo;
	if (NULL != (friendinfo = session.friends.getElement(uid))) {
	    *friendinfo = element; // 有时，会先收到好友列表包
	} else {
	    session.friends.insertElement(uid, new FriendInfo(element));
	}
    }
}

    template<typename T>
inline void QQSession::SetGroupsListName<T>::operator()
( const typename T::value_type& element)
{
    uint32_t groupid = element.group;
    if (DEFAULT_GROUP != groupid) {
	GroupInfo* groupinfo;
	if (NULL != (groupinfo = session.groups.getElement(groupid))) {
	    groupinfo->setNickName(g2u.convert(element.groupname));
	} else {
	    session.groups.insertElement(groupid,
		    new GroupInfo(groupid, g2u.convert(element.groupname)));
	}
    }

}
    template <typename T>
inline void QQSession::SetBuddysList<T>::operator ()
(const typename T::value_type& element)
{
    FriendInfo* value = 
	friends.getElement(getElementId(element));

    if (NULL == value)
	friends.insertElement(getElementId(element),
		new FriendInfo(element));
    else 
	*value = element;
}
    template <typename T>
inline void QQSession::SetUsersList<T>::operator ()
(const typename T::value_type& element)
{
    QQOnlineUserInfo* value = 
	users.getElement(getElementId(element));

    if (NULL == value)
	users.insertElement(getElementId(element),
		new QQOnlineUserInfo(element));
    else 
	*value = element;
}


#endif // _QQ_SESSION_H_

